package com.linkhub.oms.admin.modules.whitelist.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.linkhub.oms.admin.common.util.I18nMessageUtil;
import com.linkhub.oms.admin.modules.satoken.session.LhSessionContext;
import com.linkhub.oms.admin.modules.whitelist.domain.param.WhiteListInsertParam;
import com.linkhub.oms.admin.modules.whitelist.domain.vo.WhiteListApprovalVO;
import com.linkhub.oms.admin.modules.whitelist.service.WhiteListApprovalBizService;
import com.linkhub.oms.admin.modules.whitelist.service.WhiteListBizService;
import com.linkhub.oms.common.constant.QtRegexPool;
import com.linkhub.oms.common.enums.IEnumConvert;
import com.linkhub.oms.common.enums.mp.ApprovalStatusEnum;
import com.linkhub.oms.common.enums.mp.ProcessStatusEnum;
import com.linkhub.oms.common.exception.BizException;
import com.linkhub.oms.common.response.PageDTO;
import com.linkhub.oms.common.util.CacheKeyAssist;
import com.linkhub.oms.common.util.QtStreamUtil;
import com.linkhub.oms.component.redis.locker.DistributedLocker;
import com.linkhub.oms.component.transactional.service.TransactionalCallBackService;
import com.linkhub.oms.dao.mpservice.IUserService;
import com.linkhub.oms.dao.mpservice.IWhiteListApprovalRecordService;
import com.linkhub.oms.entity.UserEntity;
import com.linkhub.oms.entity.WhiteListApprovalRecordEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.linkhub.oms.common.constant.SaTokenConstant.CUSTOMER_WHITE_LIST;

/**
 * @Description: WhiteListApprovalBizService
 * @Author: echo
 * @Date: 2024/3/8 21:59
 * @Version: 1.0
 */
@Slf4j
@Service
public class WhiteListApprovalBizServiceImpl implements WhiteListApprovalBizService {

    @Resource
    private IWhiteListApprovalRecordService whiteListApprovalRecordService;
    @Resource
    private DistributedLocker distributedLocker;
    @Resource
    private WhiteListBizService whiteListBizService;
    @Resource
    private IUserService userService;
    @Resource
    private TransactionalCallBackService transactionalCallBackService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void applyVipCustomer(String email) {
        // TODO: echo_1.0 是否可以重复申请 当被拒绝或已存在待审批记录时
        boolean exists = whiteListApprovalRecordService.exists(email);
        if (exists) {
            return;
        }

        whiteListApprovalRecordService.save(createDefaultApprovalRecord(email));
    }

    @Override
    public PageDTO<WhiteListApprovalVO> pageWhiteListApprovals(String keyword,
                                                               Integer processStatus,
                                                               Integer page,
                                                               Integer size) {

        ProcessStatusEnum status = ProcessStatusEnum.PENDING;
        if (Objects.isNull(processStatus)) {
            status = null;

        }
        Page<WhiteListApprovalRecordEntity> recordPage = whiteListApprovalRecordService.lambdaQuery()
                .eq(Objects.nonNull(status), WhiteListApprovalRecordEntity::getProcessStatus, status)
                .like(StringUtils.isNotBlank(keyword), WhiteListApprovalRecordEntity::getEmail, keyword)
                .orderByAsc(WhiteListApprovalRecordEntity::getApprovalStatus)
                .orderByDesc(WhiteListApprovalRecordEntity::getCreatedTime)
                .page(new Page<>(page, size));

        PageDTO<WhiteListApprovalVO> res = new PageDTO<>();
        res.setTotal(recordPage.getTotal());
        res.setSize(recordPage.getSize());
        res.setCurrent(recordPage.getCurrent());
        res.setPages(recordPage.getPages());
        res.setRecords(QtStreamUtil.map(recordPage.getRecords(), this::convert));

        return res;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approveById(Long id, Integer approvalStatus) {
        // 判断记录是否待审批
        WhiteListApprovalRecordEntity approvalRecord = whiteListApprovalRecordService.getPendingApprovalRecord(id);
        if (Objects.isNull(approvalRecord)) {
            throw new BizException(I18nMessageUtil.getMessage("whitelist.approval.record.absent"));
        }
        String email = approvalRecord.getEmail();

        // 加锁
        String lockKey = CacheKeyAssist.getWhiteListApprovalLockKey(email);
        if (!distributedLocker.tryLock(lockKey)) {
            throw new BizException(I18nMessageUtil.getMessage("whitelist.approval.concurrence.problem"));
        }

        try {
            // 双检锁，确保有待处理记录才往下执行
            if (Objects.isNull(whiteListApprovalRecordService.getPendingApprovalRecord(id))) {
                return;
            }

            // 更新审批记录
            updateApprovalRecordEntity(approvalStatus, approvalRecord);
            whiteListApprovalRecordService.updateById(approvalRecord);

            if (ApprovalStatusEnum.REFUSE.equals(approvalRecord.getApprovalStatus())) {
                log.info("approveById, email={}, status={}", email, ApprovalStatusEnum.REFUSE.getDesc());
                return;
            }
            // 添加被审批用户到白名单
            whiteListBizService.addUserToWhiteList(email);

            // 事务提交后
            transactionalCallBackService.syncHandleAfterCommit(this, service -> {
                UserEntity user = userService.getUserByEmail(email);
                LhSessionContext.removeUserSessionKey(user.getUserId(), CUSTOMER_WHITE_LIST);
            });

        } finally {
            // 释放锁
            distributedLocker.unlock(lockKey);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addBatch(WhiteListInsertParam whiteListInsertParam) {
        // 过滤出符合的邮箱
        Set<String> formEmails = convertToEmails(whiteListInsertParam.getContent());
        // 过滤已存在白名单的邮箱
        List<String> emails = filterIfPresent(formEmails);
        if (CollUtil.isEmpty(emails)) {
            return;
        }

        // 加锁
        List<String> multiLockKeys = QtStreamUtil.map(emails, CacheKeyAssist::getWhiteListApprovalLockKey);
        if (!distributedLocker.tryLockMulti(multiLockKeys)) {
            throw new BizException(I18nMessageUtil.getMessage("whitelist.approval.concurrence.problem"));
        }

        try {
            // 双检锁
            List<String> absentEmails = filterIfPresent(emails);
            if (CollUtil.isEmpty(absentEmails)) {
                return;
            }
            // 查询出待审批的记录
            Map<String, WhiteListApprovalRecordEntity> pendingApproveEmails = getPendingApproveEmails(absentEmails);
            Map<Boolean, List<String>> saveOrUpdateMap = QtStreamUtil.partition(absentEmails,
                    pendingApproveEmails::containsKey);

            // save
            saveBatchRecords(saveOrUpdateMap.get(false));
            // update
            updateBatchRecords(pendingApproveEmails.values());

            // 添加到白名单
            whiteListBizService.addBatchToWhiteList(absentEmails);

            // 事务提交后
            transactionalCallBackService.syncHandleAfterCommit(this, service -> {
                // 删除对应白名单缓存
                List<UserEntity> users = userService.listUserByEmails(absentEmails);
                QtStreamUtil.peek(users, user -> LhSessionContext.removeUserSessionKey(user.getUserId(),
                        CUSTOMER_WHITE_LIST));

            });
        } finally {
            distributedLocker.unLockMulti(multiLockKeys);
        }
    }

    private void saveBatchRecords(List<String> emails) {
        if (CollUtil.isEmpty(emails)) {
            return;
        }
        List<WhiteListApprovalRecordEntity> saveList = QtStreamUtil.map(emails, this::createAgreeApprovalRecord);
        whiteListApprovalRecordService.saveBatch(saveList);

    }

    private void updateBatchRecords(Collection<WhiteListApprovalRecordEntity> records) {
        if (CollUtil.isEmpty(records)) {
            return;
        }
        for (WhiteListApprovalRecordEntity record : records) {
            updateApprovalRecordEntity(ApprovalStatusEnum.AGREE.getCode(), record);
        }

        whiteListApprovalRecordService.updateBatchById(records);
    }

    private Map<String, WhiteListApprovalRecordEntity> getPendingApproveEmails(List<String> absentEmails) {
        List<WhiteListApprovalRecordEntity> approvalRecords = whiteListApprovalRecordService.listPendingApprovalRecords(absentEmails);
        return QtStreamUtil.toMap(approvalRecords, WhiteListApprovalRecordEntity::getEmail);
    }

    private List<String> filterIfPresent(Collection<String> formEmails) {
        Set<String> existsEmails = whiteListBizService.listExistsEmails(formEmails);
        return QtStreamUtil.filter(formEmails, email -> !existsEmails.contains(email));
    }

    private Set<String> convertToEmails(String content) {
        String[] emailArr = content.split(QtRegexPool.LF_CR);
        return Stream.of(emailArr)
                .filter(Validator::isEmail)
                .collect(Collectors.toSet());
    }


    private void updateApprovalRecordEntity(Integer approvalStatus, WhiteListApprovalRecordEntity approvalRecord) {
        ApprovalStatusEnum approvalStatusEnum = IEnumConvert.valueOf(approvalStatus, ApprovalStatusEnum.class);
        approvalRecord.setApprovalStatus(approvalStatusEnum);
        approvalRecord.setProcessStatus(ProcessStatusEnum.PROCESSED);

        approvalRecord.setCreatedTime(null);
        approvalRecord.setUpdateTime(null);
        approvalRecord.setOperatorId(LhSessionContext.getUserId());
        approvalRecord.setOperatorTime(LocalDateTime.now());
    }

    private WhiteListApprovalVO convert(WhiteListApprovalRecordEntity entity) {
        WhiteListApprovalVO vo = new WhiteListApprovalVO();
        vo.setId(entity.getId());
        vo.setEmail(entity.getEmail());
        vo.setApplyTime(entity.getCreatedTime());
        vo.setApprovalStatus(entity.getApprovalStatus().getCode());
        vo.setApprovalStatusDesc(entity.getApprovalStatus().getDesc());
        return vo;
    }

    private WhiteListApprovalRecordEntity createAgreeApprovalRecord(String email) {
        WhiteListApprovalRecordEntity entity = new WhiteListApprovalRecordEntity();
        entity.setEmail(email);
        entity.setApprovalStatus(ApprovalStatusEnum.AGREE);
        entity.setProcessStatus(ProcessStatusEnum.PROCESSED);
        entity.setOperatorId(LhSessionContext.getUserId());
        entity.setOperatorTime(LocalDateTime.now());
        entity.setCreatedTime(LocalDateTime.now());
        entity.setUpdateTime(LocalDateTime.now());
        return entity;
    }

    private WhiteListApprovalRecordEntity createDefaultApprovalRecord(String email) {
        WhiteListApprovalRecordEntity entity = new WhiteListApprovalRecordEntity();
        entity.setEmail(email);
        entity.setApprovalStatus(ApprovalStatusEnum.PENDING_APPROVAL);
        entity.setProcessStatus(ProcessStatusEnum.PENDING);
        entity.setCreatedTime(LocalDateTime.now());
        entity.setUpdateTime(LocalDateTime.now());
        return entity;
    }
}
